import numpy as np
import pandas as pd
import tkinter as tk
from core.constant import *
from conf import conf
from tools.k_lines import round_rectangle, value2px


class EleWinView:
    """元素窗口视图类父类"""

    def __init__(self, super_view, frame, ele_model):
        self.super_view = super_view
        self.ele_model = ele_model
        self.frame = frame
        self.draw_main_canvas = None
        self.x_axis_canvas = None
        self.y_axis_canvas = None
        self.k_blank = self.ele_model.k_blank
        self.k_shadow = self.ele_model.k_shadow
        self.k_cir = self.ele_model.k_cir
        self.k_line_size = self.ele_model.k_line_size  # k线影线size
        self.lines_size = self.ele_model.lines_size  # 线条size

    def sec_init(self):
        # self.draw_main_canvas = tk.Canvas(self.frame)
        self.draw_main_canvas = tk.Canvas(self.frame, width=self.ele_model.ele_size[0],
                                          height=self.ele_model.ele_size[1])
        self.draw_main_canvas.grid(row=0, column=0, sticky="nsew")
        # 绑定鼠标移动事件
        self.draw_main_canvas.bind("<Motion>", self.show_cross_line)

    def draw_factor_line(self, factor, color):
        k_cont_width = self.ele_model.super_model.console_model.k_cont_width
        # 绘制line
        k_x = 0
        line_points = []
        for index, o in self.ele_model.factor_px_df.iterrows():
            x = k_x + k_cont_width / 2
            y = o[factor]
            line_points.append((x, y))
            k_x += k_cont_width

            if len(line_points) >= 2:
                x1 = line_points[-2][0] - self.k_blank / 2  # 由于K线右侧空出了self.k_blank，故相应指标向左偏移 - self.k_blank / 2
                x2 = line_points[-1][0] - self.k_blank / 2  # 由于K线右侧空出了self.k_blank，故相应指标向左偏移 - self.k_blank / 2
                y1 = line_points[-2][1]
                y2 = line_points[-1][1]
                self.draw_main_canvas.create_line(x1, y1, x2, y2, width=self.lines_size, fill=color)
        pass

    def draw_factor_point(self, factor, color):
        k_cont_width = self.ele_model.super_model.console_model.k_cont_width
        # 绘制点
        # 点的半径
        radius = k_cont_width / 4
        # 绘制point
        k_x = 0
        for index, o in self.ele_model.factor_px_df.iterrows():
            x = k_x + k_cont_width / 2 - self.k_blank / 2  # 由于K线右侧空出了self.k_blank，故相应指标向左偏移 - self.k_blank / 2
            y = o[factor]
            k_x += k_cont_width
            if np.isnan(y):
                pass
            else:
                self.draw_main_canvas.create_oval((x - radius, y - radius, x + radius, y + radius), fill=color,
                                                  outline=color, width=0)
        pass

    def draw_factor_tank(self, color_ls=None, factor='tank1', draw=False):
        if color_ls is None:
            color_ls = ['red', 'green']
        if self.ele_model.max_min_val_ls[0] > 0 > self.ele_model.max_min_val_ls[1]:
            px_0 = value2px(0, self.ele_model.max_min_val_ls, self.ele_model.y_axis_size[1])
        else:
            if self.ele_model.max_min_val_ls[1] > 0:
                px_0 = value2px(self.ele_model.max_min_val_ls[1], self.ele_model.max_min_val_ls, self.ele_model.y_axis_size[1])
            elif self.ele_model.max_min_val_ls[0] < 0:
                px_0 = value2px(self.ele_model.max_min_val_ls[0], self.ele_model.max_min_val_ls, self.ele_model.y_axis_size[1])
            else:
                raise ValueError("因子值范围错误，逻辑错误。")
        # 箱体的宽度
        k_cont_width = self.ele_model.super_model.console_model.k_cont_width
        tank_width = k_cont_width
        # 绘制箱体
        k_x = 0
        for index, o in self.ele_model.factor_px_df.iterrows():
            if o[factor] <= px_0:  # 因子值在0轴以上
                color = color_ls[0]
            else:  # 因子值在0轴以下
                color = color_ls[1]

            x1 = k_x
            y1 = px_0
            x2 = k_x + tank_width
            y2 = o[factor]
            points = round_rectangle(x1, y1, x2, y2, radius=self.k_cir)
            self.draw_main_canvas.create_polygon(points, fill=color, smooth=True)

            k_x += k_cont_width

    def draw_y_line(self):
        # 补充绘制y轴线
        x1 = 0
        x2 = self.ele_model.ele_size[0]
        for y_axis_px in self.ele_model.y_axis_px_ls:
            y1 = y2 = y_axis_px[1]
            self.draw_main_canvas.create_line(x1, y1, x2, y2, width=1, fill="#D3D3D3")

    def show_attach(self):
        self.show_x_axis()
        self.show_y_axis()
        self.show_legend()

    def show_x_axis(self):
        if self.ele_model.show_x_axis:
            pass
        else:
            return
        self.x_axis_canvas = tk.Canvas(self.frame, width=self.ele_model.x_axis_size[0],
                                       height=self.ele_model.x_axis_size[1])
        self.x_axis_canvas.grid(row=1, column=0)

        k_num = len(self.ele_model.draw_data)  # 当前窗口绘制k线的数量
        # 每根k线对应的像素值
        k_px = self.ele_model.super_model.console_model.k_cont_width
        # 一个坐标轴绘制间隔内的k线数量
        k_num_interval = conf.KChart.x_axis_interval.value // k_px
        # 获取绘制点坐标及其数值
        draw_px_ls = []
        draw_text_ls = []
        draw_num = k_num // k_num_interval  # 获取最多绘制数量
        for i in range(draw_num):
            if i == draw_num - 1:
                continue  # 最后一个不绘制，为了美观
            # 绘制文本的坐标
            text_px_x = (i + 1) * k_num_interval * k_px - k_px / 2
            text_px_y = self.ele_model.x_axis_size[1] / 2
            draw_px_ls.append([text_px_x, text_px_y])
            # 绘制文本的数值，此时记录datetime类型的时间数据
            text_dt = self.ele_model.draw_data.iloc[i * k_num_interval].name  # 定位到该位置对应的k线数据并获取相应series的name
            draw_text_ls.append(text_dt)
        # 处理draw_text_ls，判断k线的级别
        interval_ts_ls = []
        index_ls = self.ele_model.draw_data.index.tolist()
        for i, o in enumerate(index_ls):
            if i == 0:
                continue
            interval_ts_ls.append(index_ls[i].timestamp() - index_ls[i - 1].timestamp())

        interval_ts_dc = {}
        for i, o in enumerate(interval_ts_ls):
            if o in interval_ts_dc.keys():
                interval_ts_dc[o] += 1
            else:
                interval_ts_dc[o] = 1

        key_ls = list(interval_ts_dc.keys())
        val_ls = list(interval_ts_dc.values())
        arg_max = np.argmax(val_ls)
        interval_ts = key_ls[arg_max]
        if interval_ts < 60:  # 如果小于一分钟
            # 展示 月-日 时：分
            for i, o in enumerate(draw_text_ls):
                draw_text_ls[i] = o.strftime("%d %H:%M:%S")

        elif interval_ts < 60 * 60 * 24:  # 如果小于一天
            # 展示 月-日 时：分
            for i, o in enumerate(draw_text_ls):
                draw_text_ls[i] = o.strftime("%m-%d %H:%M")
        else:
            # 展示 年-月-日
            for i, o in enumerate(draw_text_ls):
                draw_text_ls[i] = o.strftime("%Y-%m-%d")
        # 画到图标上
        for i, o in enumerate(draw_text_ls):
            text_x = draw_px_ls[i][0]
            text_y = draw_px_ls[i][1]
            self.x_axis_canvas.create_text(text_x, text_y, text=o, font=conf.KChart.font.value, fill="black")
            # 绘制|表示刻度
            self.x_axis_canvas.create_text(text_x, 0, text='|', font=conf.KChart.font.value, fill="black")

    def show_y_axis(self):
        if self.ele_model.show_y_axis:
            pass
        else:
            return
        self.y_axis_canvas = tk.Canvas(self.frame, width=self.ele_model.y_axis_size[0],
                                       height=self.ele_model.y_axis_size[1])
        self.y_axis_canvas.grid(row=0, column=1)

        y_axis_text_ls = self.ele_model.y_axis_text_ls
        y_axis_px_ls = self.ele_model.y_axis_px_ls

        for i, text_o in enumerate(y_axis_text_ls):
            text = text_o
            text_x = y_axis_px_ls[i][0]
            text_y = y_axis_px_ls[i][1]
            self.y_axis_canvas.create_text(text_x, text_y, text=text, font=conf.KChart.font.value, fill="black")
            # 绘制一根-表示刻度线
            self.y_axis_canvas.create_text(0, text_y, text='--', font=conf.KChart.font.value, fill="black")
        pass

    def show_legend(self):
        if self.ele_model.show_legend and self.ele_model.legend_ls:
            pass
        else:
            return
        # 生成框的坐标
        high = 18 * len(self.ele_model.legend_ls)
        # text_cont_ax = [self.win_size[0]-100, 20, self.win_size[0]-20, 20+high]
        text_cont_ax = [20, 20, 120, 20 + high]
        # 创建文本框
        # text_cont_ax[0], text_cont_ax[1], text_cont_ax[2], text_cont_ax[3]
        self.draw_main_canvas.create_rectangle(text_cont_ax[0], text_cont_ax[1], text_cont_ax[2], text_cont_ax[3],
                                               width=1,
                                               fill="white")
        # 框内写图例
        # ele_type类型： bar：绘制k线 volume：绘制交易量柱 line：绘制线条 point：绘制点 tank：绘制箱体
        # line_byk：绘制线条 point_byk：绘制点  // 区别在于byk以k线最大最小值为准，否则以对应指标最大最小值为准
        for i, o in enumerate(self.ele_model.legend_ls):
            name = o[0]
            if o[2] == FactorShowCategory.Bar:
                shape = '[]'
            elif o[2] == FactorShowCategory.Volume:
                shape = '##'
            elif o[2] == FactorShowCategory.Line:
                shape = '--'
            elif o[2] == FactorShowCategory.Point:
                shape = 'oo'
            elif o[2] == FactorShowCategory.Tank:
                shape = '[]'
            # elif o[2] == 'line_byk':
            #     shape = '--'
            # elif o[2] == 'point_byk':
            #     shape = 'oo'
            elif o[2] == FactorShowCategory.Buy:
                shape = 'B'
            elif o[2] == FactorShowCategory.Sell:
                shape = 'S'
            else:
                shape = '??'
            color = o[1]
            self.draw_main_canvas.create_text(text_cont_ax[0] + text_cont_ax[2] / 2 - 10, text_cont_ax[1] + 9 + i * 18,
                                              text=name + ':' + shape, font=conf.KChart.font.value, fill=color)

    def show_cross_line(self, event):
        is_show_crosshair = self.ele_model.super_model.console_model.is_show_crosshair
        time_span = self.ele_model.super_model.console_model.time_span
        change_range = self.ele_model.super_model.console_model.change_range
        if is_show_crosshair:

            sub_view_ls = ([self.ele_model.super_model.super_view.k_lines_view,
                            self.ele_model.super_model.super_view.volume_view]
                           + self.ele_model.super_model.super_view.factor_view_ls)
            # 遍历子窗口
            for o in sub_view_ls:
                o: EleWinView = o
                # 删除旧的十字线
                o.draw_main_canvas.delete("crosshair")
                # 绘制竖向的十字线
                # 竖线上端y值
                y_top = 0

                # 竖线下端y值
                y_bottom = o.draw_main_canvas.winfo_height()

                o.draw_main_canvas.create_line(event.x, y_top, event.x, y_bottom, fill="black", dash=(4, 2),
                                               tags="crosshair")
            if self in sub_view_ls:
                # 绘制当前窗口的横向十字线
                x_left = 0
                y_left = event.widget.winfo_width()
                self.draw_main_canvas.create_line(x_left, event.y, y_left, event.y, fill="black", dash=(4, 2),
                                                  tags="crosshair")
                # 绘制横向实线
                time_span_px = time_span * self.ele_model.super_model.console_model.k_cont_width
                self.draw_main_canvas.create_line(event.x-time_span_px, event.y, event.x, event.y, fill="blue", width=2, tags="crosshair")
                # 绘制竖向实现
                width = self.ele_model.super_model.k_lines_model.ele_size[1]
                max_min_val_ls = self.ele_model.super_model.k_lines_model.max_min_val_ls
                if max_min_val_ls[0] - max_min_val_ls[1] != 0:
                    rate = width / (max_min_val_ls[0] - max_min_val_ls[1])
                else:
                    rate = 0
                change_range_px = change_range * rate
                self.draw_main_canvas.create_line(event.x, event.y, event.x, event.y+change_range_px, fill="blue", width=2, tags="crosshair")

            pass
        else:
            pass


class KLinesWinView(EleWinView):
    def __init__(self, super_view, frame, ele_model):
        super().__init__(super_view, frame, ele_model)

    def draw_k(self):
        """绘制k线"""
        k_cont_width = self.ele_model.super_model.console_model.k_cont_width

        k_x = 0
        for index, o in self.ele_model.k_px_df.iterrows():
            x1 = k_x
            x2 = x1 + k_cont_width - self.k_blank

            if self.ele_model.sub_c_o_sr.loc[index] >= 0:
                y1 = o['close']
                y2 = o['open']
                color = conf.KChart.k_color_ls.value[0]
            else:
                y1 = o['open']
                y2 = o['close']
                color = conf.KChart.k_color_ls.value[1]

            # 绘制阴影
            color_shadow = 'black'
            points_shadow = round_rectangle(x1 - self.k_shadow, y1 + self.k_shadow, x2 - self.k_shadow,
                                            y2 + self.k_shadow, radius=self.k_cir)
            self.draw_main_canvas.create_polygon(points_shadow, fill=color_shadow, smooth=True)
            # 绘制影线
            lx1 = (x1 + x2) / 2 - self.k_blank / 2  # 由于K线右侧空出了self.k_blank，故相应指标向左偏移 - self.k_blank / 2
            lx2 = (x1 + x2) / 2 - self.k_blank / 2  # 由于K线右侧空出了self.k_blank，故相应指标向左偏移 - self.k_blank / 2
            ly1 = o['high']
            ly2 = o['low']
            self.draw_main_canvas.create_line(lx1, ly1, lx2, ly2, width=self.k_line_size, fill='black')
            # 绘制实体k线
            points = round_rectangle(x1, y1, x2, y2, radius=self.k_cir)
            self.draw_main_canvas.create_polygon(points, fill=color, smooth=True)
            # k_x +=
            k_x += k_cont_width

    def draw_buy(self):
        """绘制交易位置"""
        k_cont_width = self.ele_model.super_model.console_model.k_cont_width
        # 点的半径
        radius = (k_cont_width / 2) * 1.8
        factor = "buy"
        if factor not in self.ele_model.factor_px_df.columns:
            return
        color = "#FFA07A"
        # 绘制point
        k_x = 0
        for index, o in self.ele_model.factor_px_df.iterrows():
            # 如果数据为空，就跳过
            if np.isnan(o[factor]):
                k_x += k_cont_width
                continue
            x = k_x + k_cont_width / 2 - self.k_blank / 2  # 由于K线右侧空出了self.k_blank，故相应指标向左偏移 - self.k_blank / 2
            y = o[factor]
            k_x += k_cont_width
            # 绘制b
            self.draw_main_canvas.create_oval((x - radius, y - radius, x + radius, y + radius), fill=color,
                                              outline=color,
                                              width=0)
            self.draw_main_canvas.create_text(x, y, text='B', font=conf.View.font.value, fill="red")

    def draw_sell(self):
        """绘制交易位置"""
        k_cont_width = self.ele_model.super_model.console_model.k_cont_width
        # 点的半径
        radius = (k_cont_width / 2) * 1.8
        factor = "sell"
        if factor not in self.ele_model.draw_data.columns:
            return
        color = "#90EE90"
        # 绘制point
        k_x = 0
        for index, o in self.ele_model.k_px_df.iterrows():
            # 如果数据为空，就跳过
            if np.isnan(o[factor]):
                k_x += k_cont_width
                continue
            x = k_x + k_cont_width / 2 - self.k_blank / 2  # 由于K线右侧空出了self.k_blank，故相应指标向左偏移 - self.k_blank / 2
            y = o[factor]
            k_x += k_cont_width
            # 绘制b
            self.draw_main_canvas.create_oval((x - radius, y - radius, x + radius, y + radius), fill=color,
                                              outline=color,
                                              width=0)
            self.draw_main_canvas.create_text(x, y, text='S', font=conf.View.font.value, fill="green")

    def draw_mess(self):
        """绘制消息位置"""
        k_cont_width = self.ele_model.super_model.console_model.k_cont_width
        # 点的半径
        radius = (k_cont_width / 2) * 1.8
        factor = SigType.Mess.value
        if factor not in self.ele_model.draw_data.columns:
            return
        color = "#ADD8E6"
        # 绘制point
        k_x = 0
        for index, o in self.ele_model.k_px_df.iterrows():
            # 如果数据为空，就跳过
            if np.isnan(o[factor]):
                k_x += k_cont_width
                continue
            x = k_x + k_cont_width / 2 - self.k_blank / 2  # 由于K线右侧空出了self.k_blank，故相应指标向左偏移 - self.k_blank / 2
            y = o[factor]
            k_x += k_cont_width
            # 绘制b
            self.draw_main_canvas.create_oval((x - radius, y - radius, x + radius, y + radius), fill=color,
                                              outline=color,
                                              width=0)
            self.draw_main_canvas.create_text(x, y, text='M', font=conf.View.font.value, fill="blue")


class VolumeWinView(EleWinView):
    def __init__(self, super_view, frame, ele_model):
        super().__init__(super_view, frame, ele_model)

    def draw_volume(self):
        """绘制volume"""
        k_cont_width = self.ele_model.super_model.console_model.k_cont_width
        k_x = 0
        for index, o in self.ele_model.volume_px_df.iterrows():
            x1 = k_x
            x2 = x1 + k_cont_width - self.k_blank
            if self.ele_model.sub_c_o_sr.loc[index] >= 0:
                y1 = o['volume']
                y2 = self.ele_model.ele_size[1]
                color = conf.KChart.k_color_ls.value[0]
            else:
                y1 = o['volume']
                y2 = self.ele_model.ele_size[1]
                color = conf.KChart.k_color_ls.value[1]
            # 绘制阴影
            color_shadow = 'black'
            points_shadow = round_rectangle(x1 - self.k_shadow, y1 + self.k_shadow, x2 - self.k_shadow,
                                            y2 + self.k_shadow, radius=self.k_cir)
            self.draw_main_canvas.create_polygon(points_shadow, fill=color_shadow, smooth=True)
            # 绘制实体volume线
            points = round_rectangle(x1, y1, x2, y2, radius=self.k_cir)
            self.draw_main_canvas.create_polygon(points, fill=color, smooth=True)
            # k_x +=
            k_x += k_cont_width


class FactorWinView(EleWinView):
    def __init__(self, super_view, frame, ele_model):
        super().__init__(super_view, frame, ele_model)
